package analisis.enrichAnalysis;

import archivos.Txt;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import utils.OS;

/**
 * @file EnrichAnalysisGO.java
 * @author Juan Humanes Ferrer
 * @date 04-Marzo-2014
 */
public class EnrichAnalysisGO implements IEnrichAnalysis {

    private String org;
    private String ruta;
    private String sesion;
    private String namespace;
    private String genes;

    private List<String> terminos;

    private List<Double> pValues; //Valor p-value
    private List<Double> bonferronis; //Corrección bonferoonis
    private List<Double> bonferroniStepDown; //Corrección bonferoonisStepDown

    /**
     * Método que retorna la lista de terminos enriquecidos
     *
     * @return terminos
     */
    public List<String> getTerminos() {
        return terminos;
    }

    /**
     * Método que modifica la lista de terminos enriquecidos
     *
     * @param terminos
     */
    public void setTerminos(List<String> terminos) {
        this.terminos = terminos;
    }

    /**
     * Método que añade un termino enriquecido a la lista.
     *
     * @param termino
     */
    public void addTermino(String termino) {
        getTerminos().add(termino);
    }

    /**
     * Método que devuelve la lista pValues que se obtienen por cada termino
     * enriquecido en el analisis de enriquecimiento.
     *
     * @return pValues
     */
    @Override
    public List<Double> getpValues() {
        return pValues;
    }

    /**
     * Método que modifica la lista de pValues
     *
     * @param pValues
     */
    @Override
    public void setpValues(List<Double> pValues) {
        this.pValues = pValues;
    }

    /**
     * Método que añade un pValue a la lista.
     *
     * @param pValue
     */
    public void addpValue(Double pValue) {
        getpValues().add(pValue);
    }

    /**
     * Método que retorna la lista de correcciones BonferroniStepDown
     *
     * @return bonferroniStepDown
     */
    @Override
    public List<Double> getBonferroniStepDown() {
        return bonferroniStepDown;
    }

    /**
     * Método que modifica la lista de correcciones BonferroniStepDown
     *
     * @param bonferroniStepDown
     */
    @Override
    public void setBonferroniStepDown(List<Double> bonferroniStepDown) {
        this.bonferroniStepDown = bonferroniStepDown;
    }

    /**
     * Método que añade una correccion a la lista.
     *
     * @param bonferroniStepDown
     */
    public void addBonferroniStepDown(Double bonferroniStepDown) {
        getBonferroniStepDown().add(bonferroniStepDown);
    }

    /**
     * Método que retorna el organismo
     *
     * @return org
     */
    public String getOrg() {
        return org;
    }

    /**
     * Método que modifica el organismo
     *
     * @param org
     */
    public void setOrg(String org) {
        this.org = org;
    }

    /**
     * Método que retorna la ruta de la carpeta temporal de usuario
     *
     * @return ruta
     */
    public String getRuta() {
        return ruta;
    }

    /**
     * Método que modifica la ruta de la carpeta temporal de usuario
     *
     * @param ruta
     */
    public void setRuta(String ruta) {
        this.ruta = ruta;
    }

    /**
     * Método que retorna la sesion de usuario
     *
     * @return sesion
     */
    @Override
    public String getSesion() {
        return sesion;
    }

    /**
     * Método que modifica la sesion de usuario
     *
     * @param sesion
     */
    @Override
    public void setSesion(String sesion) {
        this.sesion = sesion;
    }

    /**
     * Método que retorna la nomenclatura de los genes
     *
     * @return namespace
     */
    public String getNamespace() {
        return namespace;
    }

    /**
     * Método que modifica la nomenclatura de los genes
     *
     * @param namespace
     */
    public void setNamespace(String namespace) {
        this.namespace = namespace;
    }

    /**
     * Método que retorna la lista de correcciones Bonferroni
     *
     * @return bonferronis
     */
    @Override
    public List<Double> getBonferronis() {
        return bonferronis;
    }

    /**
     * Método que modifica la lista de correcciones Bonferroni
     *
     * @param bonferronis
     */
    @Override
    public void setBonferronis(List<Double> bonferronis) {
        this.bonferronis = bonferronis;
    }

    /**
     * Método que añade una correccion a la lista.
     *
     * @param bonferronis
     */
    public void addBonferronis(Double bonferronis) {
        getBonferronis().add(bonferronis);
    }

    /**
     * Método retorna la lista de genes
     *
     * @return genes
     */
    public String getGenes() {
        return genes;
    }

    /**
     * Método modifica la lista de genes
     *
     * @param genes
     */
    public void setGenes(String genes) {
        this.genes = genes;
    }

    public EnrichAnalysisGO(String ruta, String sesion, String org, String namespace, String name) {
        String separator = OS.getDirectorySeparator();
        this.ruta = ruta;
        this.sesion = sesion;
        this.org = org;
        this.namespace = namespace;
        this.terminos = new ArrayList();
        this.pValues = new ArrayList();
        this.bonferronis = new ArrayList();
        this.bonferroniStepDown = new ArrayList();
        this.genes = Txt.genes(ruta + separator + sesion + separator + name);

    }

    /**
     * Método que crea y ejecuta el script de Python que se encarga de realizar
     * un analisis de enriquecimiento de terminos GO a partir de una lista de
     * Genes
     *
     * @throws java.io.IOException
     * @throws java.lang.InterruptedException
     */
    public void ScriptFA() throws IOException, InterruptedException {
        String separator = OS.getDirectorySeparator();
        String barras = OS.getDoubleDirectorySeparator();
        String raiz = OS.getPython();
        String scriptP = getRuta() + separator + getSesion() + separator + "example.py";
        File scriptPython = new File(scriptP);
        scriptPython.createNewFile();

        PrintWriter pwScript;
        String arch;
        try (FileWriter scriptW = new FileWriter(scriptP)) {
            pwScript = new PrintWriter(scriptW);
            pwScript.flush();
            pwScript.println("from __future__ import division");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("import sys");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("import os");
            pwScript.flush();
            pwScript.flush();
            pwScript.println();
            pwScript.flush();
            pwScript.flush();
            pwScript.println("script_path = os.path.dirname(os.path.abspath(sys.argv[0]))");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("lib_path = os.path.abspath(os.path.join(script_path, \"..\"))");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("sys.path.insert(0, lib_path)");
            pwScript.flush();
            pwScript.flush();
            pwScript.println();
            pwScript.flush();
            pwScript.flush();
            pwScript.println("from funcassociate.client import FuncassociateClient");
            pwScript.flush();
            pwScript.flush();
            pwScript.println();
            pwScript.flush();
            pwScript.flush();
            pwScript.println("c = FuncassociateClient()");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("response = c.functionate(query=(" + getGenes() + ""
                    + "), species=\"" + getOrg() + "\""
                    + ", namespace=\"" + getNamespace() + "\")");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("info = response[\"request_info\"]");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("reps = info[\"reps\"]");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("below_detection_threshhold = \"< %f\" % (1/reps)");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("");
            pwScript.flush();
            arch = getRuta() + separator + getSesion();
            String sesionR = "";
            String[] linea2 = arch.split(barras);
            for (int i = 0; i < linea2.length; i++) {
                if (i < linea2.length - 1) {
                    sesionR += linea2[i] + barras;

                } else {
                    sesionR += linea2[i];
                }

            }
            pwScript.flush();
            pwScript.println("outfile = open(\"" + sesionR + barras + "tmp.txt\", 'a')");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("for row in response[\"over\"]:");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("\t" + "row.pop(1)");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("\t" + "if row[4] is 0:");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("\t" + "\t" + "row[4] = below_detection_threshhold");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("\t" + "outfile.write(\"\\t\".join(map(str, row)))");
            pwScript.flush();
            pwScript.flush();
            pwScript.println("outfile.close()");
            pwScript.flush();
        }
        pwScript.close();

        String a = raiz + arch + separator + "example.py";

        Process f = Runtime.getRuntime().exec(a);
        f.waitFor();

        File archivo = new File(arch + separator + "tmp.txt");

        valores(archivo);

    }

    /**
     * Método que lee los resultados del enriquecimiento de analisis del fichero
     * temporal resultante de la llamada a Python
     *
     * @param archivo
     * @throws java.lang.InterruptedException
     */
    public void valores(File archivo) throws InterruptedException {

        FileReader fr = null;
        BufferedReader br = null;

        try {
            // Apertura del fichero y creacion de BufferedReader para poder
            // hacer una lectura comoda (disponer del metodo readLine()).

            fr = new FileReader(archivo);
            br = new BufferedReader(fr);

            // Lectura del fichero
            String linea = br.readLine();
            String[] val = linea.split("\t");
            for (int i = 3; i < val.length; i = i + 6) {
                Double numero = 0.0;
                if (val[i].contains("e")) {
                    String[] num = val[i].split("e");
                    numero = Double.parseDouble(num[0]);
                    int exponente = Integer.parseInt(num[1]);
                    numero = numero * Math.pow(10, exponente);
                    addpValue(numero);
                } else {
                    numero = Double.parseDouble(val[i]);
                    addpValue(numero);
                }

                String termino = val[i + 3];
                termino = correctName(termino);
                addTermino(termino);

            }

            ICorreccion bonferroniCorrection = new BonferroniCorrection();
            this.bonferronis = bonferroniCorrection.correction(getpValues(), getpValues().size());

            //Se crea un objeto ICorreccion de tipo BonferroniStepDownCorrection
            ICorreccion bonferroniStepDownCorrection = new BonferroniStepDownCorrection();
            this.bonferroniStepDown = bonferroniStepDownCorrection.correction(getpValues(), getpValues().size());

        } catch (IOException e) {

        } finally {
            // En el finally cerramos el fichero, para asegurarnos
            // que se cierra tanto si todo va bien como si salta 
            // una excepcion.
            try {
                if (null != fr) {
                    fr.close();
                }
            } catch (IOException e2) {

            }
        }

    }

    /**
     * Método que formatea el nombre del termino que viene de Python
     *
     * @param nombre sin formatear
     * @return nombre
     */
    public String correctName(String nombre) {

        String[] numeros = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
        boolean fin = false;
        boolean fin2 = false;
        int cont = nombre.length();
        while (!fin) {
            int i = 0;
            fin2 = false;
            // Se eliminan los numeros que lleva el nombre al final
            while (i < numeros.length && !fin2) {
                if (nombre.substring(0, cont).endsWith(numeros[i])) {
                    nombre = nombre.substring(0, cont - 1);
                    cont--;
                    fin2 = true;
                }
                i++;

            }
            if (fin2 == false) {
                fin = true;
            }

        }
        return nombre;
    }

}
